﻿///////////////////////////////////////////////////////
//
// XDesigner.ORM 
//
// autohr : yuans
//
// date: 2011-2-23
//
// email  : yyf9989@hotmail.com
// 
//
///////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using XDesigner.ORM;
using System.Data;
using System.Collections;
using XDesigner.Common;

namespace XDesigner.ORM.DOM
{
    public abstract class ORMTypeMappingHelper
    {

        private ORMTypeInfo _ORMType = null;

        public ORMTypeInfo ORMType
        {
            get { return _ORMType; }
            set { _ORMType = value; }
        }

        private ORMEngine _Engine = null;

        public ORMEngine Engine
        {
            get { return _Engine; }
            set { _Engine = value; }
        }

        public virtual bool SupportDelayLoad
        {
            get
            {
                return false;
            }
        }

        public virtual Type InstanceType
        {
            get
            {
                if (this.ORMType == null)
                    return null;
                else
                    return this.ORMType.InstanceType;
            }
        }

        public virtual bool FillDBCommand(
            ORMContext context , 
            string commandText,
            object contextInstance,
            string ThisPropertyName,
            object ThisValue)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (context.Command == null)
            {
                throw new ArgumentNullException("context.Command");
            }
            if (commandText == null || commandText.Trim().Length == 0)
            {
                return false;// throw new ArgumentNullException("commandText");
            }
            if (contextInstance == null && context.ForGenerateCode == false )
            {
                throw new ArgumentNullException("contextInstance");
            }

            context.Command.Parameters.Clear();
            string[] items = VariableString.AnalyseVariableString(
                commandText,
                "[%",
                "%]",
                false);
            StringBuilder mySQL = new StringBuilder();
            for (int iCount = 0; iCount < items.Length; iCount++)
            {
                if ((iCount % 2) == 0)
                {
                    mySQL.Append(items[iCount]);
                }
                else
                {
                    string name = items[iCount];
                    bool useParameter = false;
                    if (context.ForGenerateCode)
                    {
                        useParameter = true;
                    }
                    if (name.StartsWith("@"))
                    {
                        useParameter = true;
                        name = name.Substring(1);
                    }
                    object pv = null;
                    if (Util.HasContent(ThisPropertyName)
                        && string.Compare(name, ThisPropertyName, true) == 0)
                    {
                        pv = ThisValue;
                    }
                    else if (string.Compare(name, "ThisValue", true) == 0)
                    {
                        pv = ThisValue;
                    }
                    else
                    {
                        ORMPropertyInfo p = this.ORMType.Properties[name];
                        if (p != null)
                        {
                            if (context.ForGenerateCode)
                            {
                                pv = p;
                            }
                            else
                            {
                                pv = this.GetPropertyValue(contextInstance, p.Name);
                            }
                        }
                    }
                    if (useParameter)
                    {

                        ORMDBCommand.AppendParameterSQL(
                           mySQL,
                           name,
                           pv,
                           context.ParameterStyle,
                           context.Command);
                    }
                    else
                    {
                        ORMDBCommand.AppendParameterSQL(
                           mySQL,
                           name,
                           pv,
                           ParameterStyle.Macro,
                           context.Command);
                    }
                }
            }//for
            context.Command.CommandText = mySQL.ToString();
            return true;
        }

        public virtual bool FillInstance(
            ORMContext context ,
            IDataReader reader,
            object instance,
            List<RelationPropertyValue> relationValues)
        {
            return false;
        }

        public virtual void SetPropertyValue(object instance, string propertyName, object Value)
        {

        }

        public virtual object GetPropertyValue(object instance, string propertyName)
        {
            return null;
        }

         

        public virtual object GetDBPropertyValue(
            ORMContext context,
            object instance,
            string propertyName )
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            
            ORMPropertyInfo property = this.ORMType.Properties[propertyName];
            if (property == null)
            {
                throw new NotSupportedException(propertyName);
            }
            
            object Value = this.GetPropertyValue(instance, property.Name);
            if (property.Relation != null)
            {
                ORMRelationStyle rs = property.Relation.RuntimeStyle;
                if (rs == ORMRelationStyle.OneToOne)
                {
                    if (property.Relation.ChildProperty != null)
                    {
                        ORMTypeMappingHelper helper = context.Engine.MappingHelpers[Value.GetType()];
                        if (helper != null)
                        {
                            object value2 = helper.GetPropertyValue(Value, property.Relation.ChildProperty.Name);
                            return value2;
                        }
                        else
                        {
                            return Value;
                        }
                    }
                }
                else if (rs == ORMRelationStyle.OneToMany)
                {
                    ORMPropertyInfo kp = property.Relation.ParentProperty;
                    object value2 = this.GetPropertyValue(instance, kp.Name);
                    return value2;
                }
                else if (rs == ORMRelationStyle.OneToManyWithKeyList)
                {
                    ORMPropertyInfo kp = property.Relation.ChildProperty;
                    StringBuilder valueList = new StringBuilder();
                    foreach (object obj in (System.Collections.IEnumerable)Value)
                    {
                        if (valueList.Length > 0)
                        {
                            valueList.Append(",");
                        }
                        ORMTypeMappingHelper helper = context.Engine.MappingHelpers[obj.GetType()];
                        object value2 = helper.GetPropertyValue(obj, kp.Name);
                        valueList.Append(value2);
                    }//foreach
                    return valueList.ToString();
                }
            }
            return Value;
        }


        public virtual string SelectCommandText
        {
            get
            {
                return null;
            }
        }

        public virtual string OrderCommandText
        {
            get
            {
                return null;
            }
        }

        public virtual object CreateInstance()
        {
            return null;
        }

        public virtual string GetPropertyCommandText(string propertyName)
        {
            return null;
        }

        public virtual object GetDBValue(IDataReader reader , string propertyName)
        {
            return DBNull.Value;
        }


        protected object InnerGetDBValue(IDataReader reader, string fieldName, string comparabilityFieldName, string comparabilityFieldName2)
        {
            int index = reader.GetOrdinal(fieldName);
            if (index >= 0)
            {
                return reader.GetValue(index);
            }
            if (comparabilityFieldName != null)
            {
                index = reader.GetOrdinal(comparabilityFieldName);
                if (index >= 0)
                {
                    return reader.GetValue(index);
                }
            }
            if (comparabilityFieldName2 != null)
            {
                index = reader.GetOrdinal(comparabilityFieldName2);
                if (index >= 0)
                {
                    return reader.GetValue(index);
                }
            }
            return DBNull.Value;
        }

        public virtual bool PrepareDeleteCommand(ORMContext context, object instance)
        {
            throw new NotSupportedException("Delete method");
        }

        public virtual bool PrepareInsertCommand(ORMContext context, object instance)
        {
            throw new NotSupportedException("Insert method");
        }

        public virtual bool PrepareUpdateCommand(ORMContext context, object instance)
        {
            throw new NotSupportedException("Update method");
        }

        public virtual void GenerateNewValue( ORMContext context , object instance )
        {
            throw new NotSupportedException("GenerateNewValue method");
        }

        public virtual object GetKeyValue(object instance)
        {
            throw new NotSupportedException("GetKeyValue method");
        }

        protected object GetKeyValueList(
            ORMContext context ,
            System.Collections.IEnumerable values ,
            string fieldName )
        {
            if (values == null)
            {
                return DBNull.Value;
            }
            else
            {
                StringBuilder myStr = new StringBuilder();
                ORMTypeMappingHelper helper = null;
                foreach (object obj in values)
                {
                    if (helper == null)
                    {
                        helper = context.Engine.MappingHelpers[obj.GetType()];
                    }
                    object keyValue = helper.GetPropertyValue(obj, fieldName);
                    if (keyValue != null && DBNull.Value.Equals(keyValue) == false )
                    {
                        if (myStr.Length > 0)
                        {
                            myStr.Append(",");
                        }
                        myStr.Append(Convert.ToString(keyValue));
                    }
                }//foreach
                return myStr.ToString();
            }
        }

        public virtual DataTable CreateDataTable()
        {
            return null;
        }

        public virtual DataRow CreateDataRow(DataTable table , object instance)
        {
            return null;
        }

        #region Convert value type ********************************************

        public static bool DBValueToBoolean(object v , string format )
        {
            if (v == null || DBNull.Value.Equals(v))
                return false;
            else
                return Convert.ToBoolean(v);
        }

        public static byte DBValueToByte(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
            {
                return (byte)0;
            }
            else
            {
                return Convert.ToByte(v);
            }
        }

        public static char DBValueToChar(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return (char)0;
            else
                return Convert.ToChar(v);
        }



        public static DateTime DBValueToDateTime(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
            {
                return DateTime.MinValue;
            }
            else
            {
                if (Util.HasContent(format))
                {
                    DateTime dtm = DateTime.MinValue;
                    if (DateTime.TryParseExact(
                        Convert.ToString(v),
                        format,
                        null, 
                        System.Globalization.DateTimeStyles.AllowWhiteSpaces, out dtm))
                    {
                        return dtm;
                    }
                    else
                    {
                        return DateTime.MinValue;
                    }
                }
                else
                {
                    return Convert.ToDateTime(v);
                }
            }
        }


        public static decimal DBValueToDecimal(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
            {
                return decimal.Zero;
            }
            else
            {
                return Convert.ToDecimal(v);
            }
        }


        public static double DBValueToDouble(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
            {
                return double.NaN;
            }
            else
            {
                return Convert.ToDouble(v);
            }
        }

        public static object DBValueToEnum(object v, Type type)
        {
            if (v == null || DBNull.Value.Equals(v))
            {
                return 0;
            }
            else
            {
                if( v is string )
                {
                    return Enum.Parse( type , ( string ) v );
                }
                else
                {
                    return Convert.ToInt32( v );
                }
            }
        }

        public static Guid DBValueToGuid(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return Guid.Empty;
            else
                return new Guid(Convert.ToString(v));
        }

        public static short DBValueToInt16(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return (short)0;
            else
                return Convert.ToInt16(v);
        }

        public static int DBValueToInt32(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return 0;
            else
                return Convert.ToInt32(v);
        }

        public static long DBValueToInt64(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return (long)0;
            else
                return Convert.ToInt64(v);
        }

        public static sbyte DBValueToSByte(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return (sbyte)0;
            else
                return Convert.ToSByte(v);
        }

        public static float DBValueToSingle(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return (float)0;
            else
                return Convert.ToSingle(v);
        }

        public static string DBValueToString(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return null;
            else
                return Convert.ToString(v);
        }

        public static ushort DBValueToUInt16(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return (ushort)0;
            else
                return Convert.ToUInt16(v);
        }

        public static uint DBValueToUInt32(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return (uint)0;
            else
                return Convert.ToUInt32(v);
        }

        public static ulong DBValueToUInt64(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
                return (ulong)0;
            else
                return Convert.ToUInt64(v);
        }

        public static byte[] DBValueToBinary(object v, string format)
        {
            if (v == null || DBNull.Value.Equals(v))
            {
                return null;
            }
            else if (Util.HasContent(format))
            {
                if (string.Compare(format, "hex", true) == 0)
                {
                    return FromHexString(Convert.ToString(v));
                }
                return Convert.FromBase64String(Convert.ToString(v));
            }
            else if (v is string)
            {
                return Convert.FromBase64String(Convert.ToString(v));
            }
            else
            {
                return (byte[])v;
            }
        }

        /// <summary>
        /// 将字节数组转换为16进制字符串
        /// </summary>
        /// <param name="bs">字节数组</param>
        /// <returns>16进制字符串</returns>
        public static string ToHexString(byte[] bs)
        {
            if (bs == null || bs.Length == 0)
                return "";
            const string hexs = "0123456789ABCDEF";
            System.Text.StringBuilder myStr = new System.Text.StringBuilder();
            int len = bs.Length;
            for (int iCount = 0; iCount < len; iCount++)
            {
                byte b = bs[iCount];
                myStr.Append(hexs[b >> 4]);
                myStr.Append(hexs[b & 15]);

                //myStr.Append( bs[iCount].ToString("X"));
            }
            return myStr.ToString();
        }

        /// <summary>
        /// 将16进制字符串转换为字节数组
        /// </summary>
        /// <param name="strText">16进制字符串</param>
        /// <returns>字节数组</returns>
        public static byte[] FromHexString(string strText)
        {
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            int iCount = 0;
            int Value = 0;
            const string hexs = "0123456789ABCDEF";
            foreach (char c in strText)
            {
                int index = hexs.IndexOf(char.ToUpper(c));
                if (index >= 0)
                {
                    if ((iCount % 2) == 0)
                    {
                        Value = index;
                    }
                    else
                    {
                        Value = Value << 4 + index;
                        ms.WriteByte((byte)Value);
                    }
                    iCount++;
                }
            }
            if (iCount > 0 && (iCount % 2) == 0)
                ms.WriteByte((byte)Value);
            return ms.ToArray();
        }

        #endregion

    }
}